﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CoreVirtuoso.Config;
using VDS.RDF;
using VDS.RDF.Parsing;
using VDS.RDF.Query;
using VirtuosoDal.Config;

namespace CoreVirtuoso.Helpers
{
    internal static class EventContentRepositoryHelper
    {
        public static string BuildQuery(QueryType queryType,
                                        string text,
                                        string geoObjectUri,
                                        string offsetAndLimitCriteria,
                                        out bool inferenceIsEnabled)
        {
            bool textFilterIsEnabled = !String.IsNullOrEmpty(text);
            bool geoFilterIsEnabled = !String.IsNullOrEmpty(geoObjectUri);

            inferenceIsEnabled = false;

            // We do not need any ordering for count query
            string orderByCriteria = queryType == QueryType.Query ? "ORDER BY ASC (?startDateTime)" : "";

            string textAssertCriteria = "";
            if (textFilterIsEnabled)
            {
                textAssertCriteria = " ?value bif:contains '@text' ";
                if (queryType == QueryType.Query)
                    textAssertCriteria += "OPTION (SCORE ?valueScore) . ";
                else if (queryType == QueryType.Count) // we do not need scoring for count query
                    textAssertCriteria += " . ";
                //we do not need any ordering for count query
                orderByCriteria = queryType == QueryType.Query ? "GROUP BY ?event ORDER BY DESC (sum(?valueScore))" : "";
            }

            string eventFromCriteria = "";
            string eventTypeAssertCriteria = "";
            string eventPropertiesAssertCriteria = "";

            string geoFromCriteria = "";
            string geoAssertCriteria = "";
            string geoFilterCriteria = "";
            if (geoFilterIsEnabled)
            {
                geoFromCriteria = "FROM @geoNamesGraph ";
                geoAssertCriteria = String.Format("?event events:city ?city . ?city geonames:inCountry ?country . ");
                geoFilterCriteria = String.Format(" && ( ?city = @geoObject || ?country = @geoObject )");
            }

            DateTimeOffset now = DateTimeOffset.Now;
            string actualityCriteria = now.ToString(XmlSpecsHelper.XmlSchemaDateTimeFormat);

            string projectionCriteria = null;
            if (queryType == QueryType.Query)
                projectionCriteria = "DISTINCT ?event";
            else if (queryType == QueryType.Count)
                projectionCriteria = "COUNT(DISTINCT ?event)";

            if (offsetAndLimitCriteria == null)
                offsetAndLimitCriteria = "";

            string query = String.Format("prefix rdf:       <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
                                         "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
                                         "prefix owl:       <http://www.w3.org/2002/07/owl#> " +
                                         "prefix  rdfs:     <http://www.w3.org/2000/01/rdf-schema#> " +
                                         "prefix events:    <http://beaware.at/ontology#> " +
                                         "prefix geonames:  <http://www.geonames.org/ontology#> " +
                                         "SELECT {9} " +
                                         "FROM @graph " +
                                         "{5} " +
                                         "{10} " +
                                         "WHERE " +
                                         "{{ " +
                                         " ?event ?property ?value . " +
                                         "{8} " +
                                         " ?event events:startDateTime ?startDateTime . " +
                                         "{0} " +
                                         "{1} " +
                                         "{6} " +
                                         " FILTER (?startDateTime >= xsd:dateTime(\"{2}\") {7})" +
                                         "}} " +
                                         "{3} " +
                                         "{4} ",
                                         eventPropertiesAssertCriteria,
                                         textAssertCriteria,
                                         actualityCriteria,
                                         orderByCriteria,
                                         offsetAndLimitCriteria,
                                         geoFromCriteria,
                                         geoAssertCriteria,
                                         geoFilterCriteria,
                                         eventTypeAssertCriteria,
                                         projectionCriteria,
                                         eventFromCriteria);

            SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
            sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));

            if (textFilterIsEnabled)
                sparqlParameterizedString.SetLiteral("@text", text);

            if (geoFilterIsEnabled)
            {
                sparqlParameterizedString.SetUri("@geoNamesGraph", new Uri(Namespaces.GeoNamesFilteringOntologyNamespace));
                sparqlParameterizedString.SetUri("@geoObject", new Uri(geoObjectUri));
            }

            return sparqlParameterizedString.ToString();
        }

        public static string GetEventPropertiesCriteria(Dictionary<string, List<INode>> eventPropertiesCriterias,
                                                        string eventUriVariable)
        {
            StringBuilder queryStringBuilder = new StringBuilder();

            int objectVariablesCounter = 0;

            foreach (string propertyUri in eventPropertiesCriterias.Keys)
                foreach (INode @object in eventPropertiesCriterias[propertyUri])
                {
                    SparqlParameterizedString query =
                       new SparqlParameterizedString(String.Format("{0} @predicate ?instance{1} . ?instance{1} a @object . ",
                                                                   eventUriVariable,
                                                                   objectVariablesCounter));
                    query.SetUri("@predicate", new Uri(propertyUri));
                    query.SetParameter("@object", @object);
                    queryStringBuilder.Append(query.ToString());

                    objectVariablesCounter++;
                }
            return queryStringBuilder.ToString();
        }
    }
}